package com.myy.auth.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.myy.auth.entity.Menu;
import com.myy.auth.mapper.MenuMapper;
import com.myy.auth.vo.MenuVo;
import com.myy.common.exception.ParamException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class MenuService extends ServiceImpl<MenuMapper, Menu> {

    /**
     * 创建菜单
     */
    @Transactional
    public String saveOrUpdate(List<MenuVo> list) {
        // 1. 参数校验
        valid(list);
        // 2. 获取现有菜单数据
        List<Menu> existingMenus = this.getBaseMapper().selectList(new LambdaQueryWrapper<Menu>()
                .eq(Menu::getSysCode, list.stream().findFirst().get().getSysCode()));
        Map<String, Menu> existingMap = existingMenus.stream()
                .collect(Collectors.toMap(Menu::getId, Function.identity()));
        // 3. 处理数据
        List<Menu> toInsert = new ArrayList<>();
        List<Menu> toUpdate = new ArrayList<>();
        for (MenuVo vo : list) {
            Menu menu = convertToEntity(vo);
            if (vo.getId() == null) {
                // 新增
                menu.setId(IdUtil.objectId());
                toInsert.add(menu);
            } else {
                // 更新
                if (!existingMap.containsKey(vo.getId())) {
                    throw new ParamException("菜单不存在: " + vo.getId());
                }
                toUpdate.add(menu);
            }
        }
        // 4. 批量操作
        if (!toInsert.isEmpty()) {
            this.saveBatch(toInsert);
        }
        if (!toUpdate.isEmpty()) {
            this.updateBatchById(toUpdate);
        }
        // 5. 处理删除（可选）
        handleDelete(existingMenus, list);
        return "操作成功";
    }

    private void valid(List<MenuVo> list) {
        Assert.notNull(list, "参数不能为空");
        // 校验重复名称
        Set<String> names = new HashSet<>();
        for (MenuVo vo : list) {
            Assert.hasText(vo.getMenuName(), "菜单名称不能为空");
            if (!names.add(vo.getMenuName())) {
                throw new ParamException("存在重复菜单名称: " + vo.getMenuName());
            }
        }
        // 校验层级关系
        validateHierarchy(list);
    }

    private void validateHierarchy(List<MenuVo> list) {
        Map<String, MenuVo> map = list.stream()
                .filter(vo -> vo.getId() != null)
                .collect(Collectors.toMap(MenuVo::getId, Function.identity()));

        for (MenuVo vo : list) {
            if (vo.getParentId() != null) {
                // 检查父菜单是否存在
                if (!map.containsKey(vo.getParentId()) &&
                        this.getBaseMapper().selectById(vo.getParentId()) == null) {
                    throw new ParamException("父菜单不存在: " + vo.getParentId());
                }
                // 检查循环引用
                String currentId = vo.getId();
                String parentId = vo.getParentId();
                while (parentId != null) {
                    if (parentId.equals(currentId)) {
                        throw new ParamException("存在循环引用");
                    }
                    MenuVo parent = map.get(parentId);
                    parentId = parent != null ? parent.getParentId() : null;
                }
            }
        }
    }
    private Menu convertToEntity(MenuVo vo) {
        Menu menu = new Menu();
        BeanUtil.copyProperties(vo, menu);
        menu.setUpdateTime(new Date());
        if (vo.getId() == null) {
            menu.setCreateTime(new Date());
        }
        return menu;
    }
    private void handleDelete(List<Menu> existing, List<MenuVo> newList) {
        Set<String> newIds = newList.stream()
                .map(MenuVo::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        List<String> toDelete = existing.stream()
                .map(Menu::getId)
                .filter(id -> !newIds.contains(id))
                .collect(Collectors.toList());
        if (!toDelete.isEmpty()) {
            // 检查是否有子菜单
            for (String id : toDelete) {
                if (this.lambdaQuery().eq(Menu::getParentId, id).exists()) {
                    throw new ParamException("存在子菜单，无法删除: " + id);
                }
            }
            this.removeBatchByIds(toDelete);
        }
    }

    /**
     * 菜单结构
     * @param systemid 全部系统or单个系统
     */
    public List<MenuVo> tree(String systemid) {
        List<MenuVo> list = this.getBaseMapper().selectList(new LambdaQueryWrapper<Menu>()
                .eq(StrUtil.isNotBlank(systemid), Menu::getSysCode, systemid))
                .stream().map(e -> toVo(e)).collect(Collectors.toList());
        return buildTree(list);

    }

    //层次构建
    /**
     * 真正的树形构建
     */
    private List<MenuVo> buildTree(List<MenuVo> all) {
        // 1. 先把所有节点放到 map 里，方便 O(1) 找到父节点
        Map<String, MenuVo> idMap = all.stream()
                .collect(Collectors.toMap(MenuVo::getId, Function.identity()));
        // 2. 最终返回的顶层节点
        List<MenuVo> root = new ArrayList<>();
        // 3. 遍历挂父子关系
        for (MenuVo node : all) {
            String parentId = node.getParentId();
            if (StrUtil.equals(parentId, "0") || parentId == null) {
                // 根节点
                root.add(node);
            } else {
                // 挂到父节点下面
                MenuVo parent = idMap.get(parentId);
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(node);
                }
            }
        }

        // 4. 如需按 sort 排序可在这里做
        root.sort(Comparator.comparingInt(MenuVo::getSort));
        root.forEach(r -> sortChildren(r));

        return root;
    }

    /**
     * 递归给子节点排序，可选
     */
    private void sortChildren(MenuVo node) {
        if (CollUtil.isNotEmpty(node.getChildren())) {
            node.getChildren().sort(Comparator.comparingInt(MenuVo::getSort));
            node.getChildren().forEach(this::sortChildren);
        }
    }

    //实体映射
    private MenuVo toVo(Menu menu){
        MenuVo menuVo = BeanUtil.toBean(menu, MenuVo.class);
        return menuVo;
    }
}
